package pseudoAssemblerCompiler.twopassassembler.view.util;

import org.apache.commons.lang.StringUtils;
import org.eclipse.jface.layout.TableColumnLayout;
import org.eclipse.jface.viewers.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author Сайфуллин А.М.
 * @date: 15.10.2011 14:27:10
 */
public class SmartCombo extends Viewer {
    public Text getEditor() {
        return editor;
    }

    private Composite composite;

    private Text editor;

    private Shell popup;


    private TableViewer dataViewer;

    /**
     * Display format
     */
    private DisplayFormat displayFormat = DefaultDisplayFormat.DEFAULT;

    /**
     * Набор исходных данных
     */
    private List data;

    /**
     * Серый текст, который отображается, если поле пустое
     */
    private String defaultText;

    /**
     * Набор данных, подготовленный для использования
     */
    private List<ComboElement> elements = Collections.EMPTY_LIST;

    /**
     * Выбранный элемент
     */
    private ComboElement selectedElement;

    /**
     * ctor
     *
     * @param parent
     * @param style
     */
    public SmartCombo(Composite parent, int style) {

        composite = new Composite(parent, SWT.NONE);
        //composite.setBackground();
        GridLayout gridLayout = new GridLayout(2, false);
        gridLayout.marginHeight = 0;
        gridLayout.marginWidth = 0;
        gridLayout.marginRight = -5;
        composite.setLayout(gridLayout);

        defaultText = "";

        editor = new Text(composite, SWT.BORDER | style);
        editor.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

//        Label label = new Label(composite, SWT.NONE);
//        label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
//        label.setImage(composite.getDisplay().getSystemImage(SWT.ICON_INFORMATION));


        //
        popup = new Shell(editor.getDisplay(), SWT.NO_FOCUS | SWT.NO_TRIM | SWT.ON_TOP);
        popup.setText("popup");
        TableColumnLayout dataViewerLayout = new TableColumnLayout();
        popup.setLayout(dataViewerLayout);
//        popup.addMouseListener(new MouseAdapter() {
//            @Override
//            public void mouseUp(MouseEvent e) {
//                System.out.println("mouse up");
//            }
//
//            @Override
//            public void mouseDown(MouseEvent e) {
//                System.out.println("mouse down");
//            }
//        });


        dataViewer = new TableViewer(popup, SWT.SINGLE | SWT.FULL_SELECTION /*| SWT.NO_FOCUS*/);
        dataViewer.getTable().setLinesVisible(false);
        dataViewer.getTable().setHeaderVisible(false);
        dataViewer.setContentProvider(new ArrayContentProvider());

        dataViewer.getControl().addMouseListener(new MouseAdapter() {
            @Override
            public void mouseUp(MouseEvent e) {
//                System.out.println("mouse up");
            }

            @Override
            public void mouseDown(MouseEvent e) {
//                System.out.println("mouse down");
            }

            @Override
            public void mouseDoubleClick(MouseEvent e) {
                IStructuredSelection structuredSelection = (IStructuredSelection) dataViewer.getSelection();
                selectedElement = (ComboElement) structuredSelection.getFirstElement();

                if (selectedElement != null) {
                    ComboElement comboElement = selectedElement;
                    editor.setText(comboElement.displayName);
                    editor.setSelection(comboElement.displayName.length());

                    fireSelectionChanged(new SelectionChangedEvent(SmartCombo.this, new StructuredSelection(selectedElement.getItem())));
                }
                hidePopup();
            }
        });


        TableViewerColumn mainColumn = new TableViewerColumn(dataViewer, SWT.NONE);
        dataViewerLayout.setColumnData(mainColumn.getColumn(), new ColumnWeightData(100));

        dataViewer.setLabelProvider(new StyledCellLabelProvider() {
            @Override
            public void update(ViewerCell cell) {
                ComboElement comboElement = (ComboElement) cell.getElement();

                cell.setText(comboElement.displayName);

                super.update(cell);
            }
        });

        dataViewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
//                System.err.println("" + event);

                IStructuredSelection structuredSelection = (IStructuredSelection) event.getSelection();
                //selectedElement = structuredSelection.getFirstElement();
                if (structuredSelection.getFirstElement() != null) {
//                    selectedElement = structuredSelection.getFirstElement();
//
//                    editor.setText(displayFormat.toString(selectedElement));
//                    editor.setSelection(editor.getText().length());
                } else {
//                    selectedElement = null;
//
//                    editor.setText(displayFormat.toString(null));
                }
            }
        });

//        dataViewer.getTable().addMouseTrackListener(new MouseTrackAdapter() {
//            @Override
//            public void mouseEnter(MouseEvent e) {
//                System.err.println("enter: " + e);
//            }
//
//            @Override
//            public void mouseExit(MouseEvent e) {
//                System.err.println("exit: " + e);
//            }
//
//            @Override
//            public void mouseHover(MouseEvent e) {
//                System.err.println("hover: " + e);
//            }
//        });
//
//        dataViewer.getTable().addFocusListener(new FocusListener() {
//            @Override
//            public void focusGained(FocusEvent e) {
//                System.err.println("xxx focusGained: " + e);
//
//            }
//
//            @Override
//            public void focusLost(FocusEvent e) {
//                System.err.println("xxx focusLost: " + e);
//
//            }
//        });


//        dataViewer.getTable().addMouseListener(new MouseAdapter() {
//
//            @Override
//            public void mouseDown(MouseEvent e) {
//                System.err.println("down: " + e);
//            }
//
//            @Override
//            public void mouseUp(MouseEvent e) {
//                System.err.println("up: " + e);
//
////                ISelection selection = dataViewer.getSelection();
////                if (selection.isEmpty()) {
////                    System.err.println("empty");
////                } else {
////                    IStructuredSelection structuredSelection = (IStructuredSelection) selection;
////                    System.err.println("selection: " + structuredSelection.getFirstElement());
////
////                    selectedElement = structuredSelection.getFirstElement();
////
////                    editor.setText(displayFormat.toString(structuredSelection.getFirstElement()));
////                    editor.setSelection(editor.getText().length());
////
////                    hidePopup();
////                }
//
//            }
//        });
//
//        dataViewer.getTable().addListener(SWT.DefaultSelection, new Listener() {
//            @Override
//            public void handleEvent(Event event) {
//                System.err.println("DefaultSelection: " + event);
//            }
//        });
//        dataViewer.getTable().addListener(SWT.Selection, new Listener() {
//            @Override
//            public void handleEvent(Event event) {
//                System.err.println("selection: " + event);
//
//            }
//        });


        editor.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                Text text = (Text) e.widget;

                if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) {
                    if (popup.getVisible()) {
                        //commit value
                        IStructuredSelection structuredSelection = (IStructuredSelection) dataViewer.getSelection();
                        selectedElement = (ComboElement) structuredSelection.getFirstElement();

                        if (selectedElement != null) {
                            ComboElement comboElement = selectedElement;
                            editor.setText(comboElement.displayName);
                            editor.setSelection(comboElement.displayName.length());

                            fireSelectionChanged(new SelectionChangedEvent(SmartCombo.this, new StructuredSelection(selectedElement.getItem())));
                        } else {
                            //editor.setText("");
                        }

//                        editor.setText(displayFormat.toString(selectedElement));
//                        editor.setSelection(editor.getText().length());

                        hidePopup();

                    } else {
                        checkForOpen(text);
                    }


                } else if (e.keyCode == SWT.ESC) {
                    hidePopup();

                } else if (e.keyCode == SWT.ARROW_UP) {
                    checkForOpen(text);

                    List data = (List) dataViewer.getInput();
                    IStructuredSelection structuredSelection = (IStructuredSelection) dataViewer.getSelection();
                    Object obj = structuredSelection.getFirstElement();
                    if (obj != null) {
                        int index = data.indexOf(obj);
//                        System.err.println("up: " + index);

                        if (index != -1 && ((index -= 1) != -1)) {
                            obj = data.get(index);
                            dataViewer.setSelection(new StructuredSelection(obj), true);
                        }
                    } else {
                        if (!data.isEmpty()) {
                            dataViewer.setSelection(new StructuredSelection(data.get(data.size() - 1)), true);
                        }

//                        System.err.println("null obj");
                    }
                    e.doit = false;

                } else if (e.keyCode == SWT.ARROW_DOWN) {
                    checkForOpen(text);

                    List data = (List) dataViewer.getInput();
                    IStructuredSelection structuredSelection = (IStructuredSelection) dataViewer.getSelection();
                    Object obj = structuredSelection.getFirstElement();
                    if (obj != null) {
                        int index = data.indexOf(obj);
//                        System.err.println("down: " + index);
                        if (index != -1 && ((index += 1) < data.size())) {
                            obj = data.get(index);
//                            System.err.println("down select: " + obj);
                            dataViewer.setSelection(new StructuredSelection(obj), true);
                        }
                    } else {
                        if (!data.isEmpty()) {
                            dataViewer.setSelection(new StructuredSelection(data.get(0)), true);
                        }

                        //System.err.println("null obj");
                    }
                    e.doit = false;

                } else if (e.keyCode == SWT.BS) {
                    checkForOpen(text);
                    //todo: поиск на основе новых данных
                } else if ((e.keyCode >= 97 && e.keyCode <= 122) ||
                        (e.keyCode >= 48 && e.keyCode <= 57) ||
                        e.keyCode == 1093 || // х
                        e.keyCode == 1098 || // ъ
                        e.keyCode == 1078 || // ж
                        e.keyCode == 1101 || // э
                        e.keyCode == 1073 || // б
                        e.keyCode == 1102    // ю
                        ) {
//                    System.out.println("попали");
                    checkForOpen(text);
                }

            }

            @Override
            public void keyReleased(KeyEvent e) {
            }


        });

        editor.addTraverseListener(new TraverseListener() {
            @Override
            public void keyTraversed(TraverseEvent e) {
                if (e.keyCode == SWT.TAB) {
//                    System.err.println("keyTraversed: " + e);
                    hidePopup();
                }
            }
        });

        editor.addFocusListener(new FocusAdapter() {

            public void focusGained(FocusEvent e) {
//                System.err.println("editor focusGained: " + e);
//                System.out.println("    " + editor.getText());
//                System.out.println("    " + editor.getText().equals(defaultText));
                if (editor.getText().equals(defaultText)) {
                    editor.setText("");
                    editor.setForeground(null);
                }
            }

            @Override
            public void focusLost(FocusEvent e) {
//                System.err.println("editor focusLost: " + e);
                if (dataViewer.getControl().isFocusControl()) {
                    hidePopup();
                }

                if (editor.getText().equals("")) {
                    editor.setText(defaultText);
                    editor.setForeground(editor.getDisplay().getSystemColor(SWT.COLOR_GRAY));
                }
            }
        });
        editor.setFocus();

        editor.addDisposeListener(new DisposeListener() {
            @Override
            public void widgetDisposed(DisposeEvent e) {
                if (popup != null && !popup.isDisposed()) {
                    popup.dispose();
                }
            }
        }

        );

        editor.getShell().addControlListener(new ControlAdapter() {
            @Override
            public void controlMoved(ControlEvent e) {
//                System.err.println("editor controlMoved: " + e);
                hidePopup();
            }
        }

        );

//        editor.addMouseListener(new MouseAdapter() {
//            @Override
//            public void mouseUp(MouseEvent e) {
//                System.err.println("editor mouseUp: " + e);
//
//                if (!popup.getVisible()) {
//                    Text text = (Text) e.widget;
//                    checkForOpen(text);
//                }
//
//            }
//        });

//        editor.addMouseWheelListener(new MouseWheelListener() {
//            @Override
//            public void mouseScrolled(MouseEvent e) {
//                System.err.println("mouseScrolled: " + e);
//            }
//        });

        editor.addVerifyListener(new

                                 VerifyListener() {
                                     @Override
                                     public void verifyText
                                             (VerifyEvent
                                                      e) {

                                         if (e.doit) {
                                             String oldValue = ((Text) e.widget).getText();
                                             String newValue;

                                             StringBuffer stringBuffer = new StringBuffer(oldValue);
                                             stringBuffer.replace(e.start, e.end, e.text);


                                             newValue = stringBuffer.toString();
//                            System.err.println("" + newValue);

                                             //Поиск и фильтрация данных
                                             if (StringUtils.isEmpty(newValue)) {
                                                 dataViewer.setInput(elements);

                                             } else {
                                                 List<ComboElement> tempList = new ArrayList<ComboElement>();

                                                 for (ComboElement comboElement : elements) {
                                                     if (StringUtils.containsIgnoreCase(comboElement.displayName, newValue)) {
                                                         tempList.add(comboElement);
                                                     }
                                                 }

                                                 dataViewer.setInput(tempList);

                                                 if (tempList.isEmpty()) {
                                                     hidePopup();
                                                 }

//                        if (!tempList.isEmpty()) {
//                            dataViewer.setSelection(new StructuredSelection(tempList.get(0)), true);
//                        }

                                             }


                                             e.doit = true;
                                         }


                                     }
                                 }

        );


    }

    private void checkForOpen(Text text) {


        //todo: fix me, please
//                System.err.println("check");
//                System.err.println("getVis: " + popup.getVisible());


        if (!popup.getVisible()) {
//            System.err.println("OpenPopup");
//                    popupVisible = true;
//                    System.err.println("set visible");

            Rectangle bounds = text.getBounds();

            popup.setLocation(text.getParent().toDisplay(bounds.x, bounds.y + bounds.height));
            popup.setSize(bounds.width, 150);

            //popup.setVisible(true);
            //System.err.println("1: " + popup.getVisible());

            showPopup();

            List data = (List) dataViewer.getInput();
            if (!data.isEmpty()) {
                dataViewer.setSelection(new StructuredSelection(data.get(0)), true);
            }

        }
    }

    private void showPopup() {
        if (!popup.getVisible()) {
            popup.setVisible(true);
//            popup.open();
        }

//        if (!popupVisible) {
//            popupVisible = true;
//
//            popup.setVisible(true);
//        }
    }

    private void hidePopup() {
//        System.err.println("test visible: " + popup.getVisible());

        if (popup.getVisible()) {

//            System.err.println("hide popup");

            //  popupVisible = false;

            popup.setVisible(false);
            //popup.close();
        }
    }

//    private boolean isPopupVisible() {
//        return popup.getVisible();
//    }


    @Override
    public Control getControl() {
        return composite;
    }

    @Override
    public Object getInput() {
        return data;
    }

    @Override
    public ISelection getSelection() {
        return dataViewer.getSelection();
    }

    public Object getSelected() {
        IStructuredSelection structuredSelection = (IStructuredSelection) dataViewer.getSelection();
        if (structuredSelection != null && structuredSelection.getFirstElement() != null) {
            selectedElement = (ComboElement) structuredSelection.getFirstElement();
            return selectedElement.getItem();
        }
        return null;
    }

    @Override
    public void refresh() {
        //do nothing
    }

    @Override
    public void setInput(Object input) {
        this.data = (List) input;

        elements = new ArrayList<ComboElement>();
        for (Object obj : this.data) {
            ComboElement comboElement = new ComboElement();
            comboElement.item = obj;
            comboElement.displayName = displayFormat.toString(obj);

            elements.add(comboElement);
        }

        dataViewer.setInput(elements);
    }

    public String getText() {
        if (editor.getText().equals(defaultText)) return "";
        return editor.getText();
    }

    public void setSelection(ISelection selection) {
        setSelection(selection, true);
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection ss = (IStructuredSelection) selection;
            editor.setText(ss.getFirstElement() == null ? "" : ss.getFirstElement().toString());
            fireSelectionChanged(new SelectionChangedEvent(this, selection));
        }

    }

    @Override
    public void setSelection(ISelection selection, boolean reveal) {
        dataViewer.setSelection(selection, reveal);
    }

    public void setDisplayFormat(DisplayFormat displayFormat) {
        if (displayFormat != null) {
            this.displayFormat = displayFormat;
        } else {
            this.displayFormat = DefaultDisplayFormat.DEFAULT;
        }
    }

    public void setPrompt(String prompt) {
        this.defaultText = prompt;
    }


    /**
     * Default display format
     */
    private static class DefaultDisplayFormat implements DisplayFormat {

        public static DisplayFormat DEFAULT = new DefaultDisplayFormat();

        @Override
        public String toString(Object o) {
            return o != null ? o.toString() : EMPTY;
        }
    }

    /**
     * Элемент
     */
    private class ComboElement {

        private Object item;

        private String displayName;

        public Object getItem() {
            return item;
        }

        public void setItem(Object item) {
            this.item = item;
        }

        public String getDisplayName() {
            return displayName;
        }

        public void setDisplayName(String displayName) {
            this.displayName = displayName;
        }
    }


}
